package com.guigu.dispatch.job;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.guigu.base.entity.TransportLine;
import com.guigu.base.entity.TransportTrips;
import com.guigu.base.entity.dto.TransportTripsTruckDriverDto;
import com.guigu.dispatch.entity.CacheLineUse;
import com.guigu.dispatch.entity.OrderClassify;
import com.guigu.dispatch.entity.OrderClassifyAttach;
import com.guigu.dispatch.entity.OrderClassifyOrder;
import com.guigu.dispatch.feign.BaseFeign;
import com.guigu.dispatch.feign.OrderFeign;
import com.guigu.dispatch.feign.WorkFeign;
import com.guigu.dispatch.service.CacheLineUseService;
import com.guigu.dispatch.service.OrderClassifyAttachService;
import com.guigu.dispatch.service.OrderClassifyOrderService;
import com.guigu.dispatch.service.OrderClassifyService;
import com.guigu.dispatch.utils.OkHttpUtils;
import com.guigu.order.entity.Order;
import com.guigu.order.entity.OrderLocation;
import com.guigu.order.entity.vo.OrderLocationVO;
import com.guigu.tms.exception.TmsException;
import com.guigu.tms.result.R;
import com.guigu.work.dto.DriverJobDTO;
import com.guigu.work.dto.TaskTransportDTO;
import com.xxl.job.core.handler.annotation.XxlJob;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.sql.Driver;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName:OrdersTransportationJob
 * @Description:订单运输
 * @Author:taoLong
 * @Date:2021/11/21:16:33 星期日
 * @email:2391611102@qq.com or 2391611102qq@gmail.com
 */
@Service
@SuppressWarnings("all")
public class OrdersTransportationJob {

    private Logger log = LoggerFactory.getLogger(OrdersTransportationJob.class);
    @Autowired
    private OrderFeign orderFeign;

    @Autowired
    private OrderClassifyOrderService orderClassifyOrderService;

    @Autowired
    private OrderClassifyService orderClassifyService;

    @Autowired
    private BaseFeign baseFeign;

    @Autowired
    private WorkFeign workFeign;

    @Autowired
    private CacheLineUseService cacheLineUseService;
    @Autowired
    private OrderClassifyAttachService orderClassifyAttachService;
    private List<String> transportOrderIds;

    /**
     * 订单运输
     * 流程:
     * 订单分类-->线路规划-->创建运输任务-->规划车次车辆-->创建司机运输作业
     *
     * @throws Exception
     */
    @XxlJob("ordersTransportation")
    @GlobalTransactional(name = "orders_transportation", rollbackFor = Exception.class, timeoutMills = 60000)
    public void ordersTransportation() throws Exception {
        List<OrderClassify> classifyArrayList = new ArrayList<>();
        List<Order> orders = new ArrayList<>();
        /**
         * 1.订单分类
         */
        orderClassification(classifyArrayList, orders);
        if (ObjectUtils.isEmpty(orders)) {
            log.error("订单分类,为空,终止本次任务调度!");
            return;
        }
        log.info("订单分类完成,结果===订单集合为:{},订单分类集合为:{}", orders, classifyArrayList);
        /**
         * 2.线路规划
         * 返回线路规划集合,用于创建运输任务
         */
        List<CacheLineUse> cacheLineUses = routePlanning(classifyArrayList, orders);
        if (CollUtil.isEmpty(cacheLineUses)) {
            log.error("线路为空,终止本次任务调度");
            return;
        }
        log.info("线路规划完成,结果为:{}", cacheLineUses);
        /**
         * 3.创建运输任务---更新运单
         */
        List<TaskTransportDTO> taskTransportDtoList = createATransportationTask(cacheLineUses);
        log.info("创建运输任务完成,结果为:{}", taskTransportDtoList);

        if (CollUtil.isEmpty(taskTransportDtoList)) {
            log.error("运输任务为null,终止本次任务调度");
        }

        /**
         * 4.规划车次车辆司机
         */
        List<DriverJobDTO> driverJobDTO = planningTheNumberOfVehicles(taskTransportDtoList);
        log.info("规划车次车辆司机完成,结果为:{}", driverJobDTO);
        if (CollUtil.isEmpty(driverJobDTO)) {
            log.error("车次车辆司机没有数据,终止本次任务调度");
        }
        /**
         * 5.创建司机运输作业
         */
        createDriverTransportationJob(driverJobDTO);
        log.info("创建司机运输作业完成!");
    }

    /**
     * 创建司机作业单
     *
     * @param driverJobDTO 司机作业对象
     */
    private void createDriverTransportationJob(List<DriverJobDTO> driverJobDTO) {
        for (DriverJobDTO dto : driverJobDTO) {
            workFeign.save(dto);
        }
    }

    /**
     * 规划车次车辆司机
     *
     * @param taskTransportDtoList 运输任务集合
     * @return
     */
    private List<DriverJobDTO> planningTheNumberOfVehicles(List<TaskTransportDTO> taskTransportDtoList) {
        List<DriverJobDTO> driverJobDTO = new ArrayList<>();
        /**
         * 根据运输任务的起始机构和结束结构查找线路
         */
        for (TaskTransportDTO dto : taskTransportDtoList) {
            DriverJobDTO jobDTO = new DriverJobDTO();
            /**
             * 获取线路id来查找所对应的车次
             */
            R<List<TransportTrips>> r = baseFeign.findByLineID(dto.getUse().getCacheLineId());
            if (ObjectUtils.isEmpty(r.getData())) {
                /**
                 * 为null代表没有数据
                 */
                return driverJobDTO;
            }
            /**
             * 获取车次数据
             */
            List<TransportTrips> transportTripsList = r.getData();
            int i = RandomUtil.randomInt(transportTripsList.size());
            /**
             * 查询出车次所对应的车次与车辆和司机关联表数据
             */
            R<List<TransportTripsTruckDriverDto>> l = baseFeign.findAllTruckDriverTransportTrips(transportTripsList.get(i).getId(), null, null);
            /**
             * 如果车次所对应的车次与车辆和司机关联表数据为null,则return
             */
            if (CollUtil.isEmpty(l.getData())) {
                return driverJobDTO;
            }
            List<TransportTripsTruckDriverDto> list = l.getData();
            int j = RandomUtil.randomInt(list.size());
            TransportTripsTruckDriverDto truckDriverDto = list.get(j);
            /**
             * 开始添加 车次车辆司机订单分类关联表
             */
            OrderClassifyAttach o = new OrderClassifyAttach();
            /**
             * 设置车次Id
             */
            o.setTruckId(truckDriverDto.getTruckId());
            /**
             * 设置车辆Id
             */
            o.setTripsId(truckDriverDto.getTransportTripsId());
            /**
             * 设置司机Id
             */
            o.setDriverId(truckDriverDto.getUserId());
            /**
             * 设置订单分类Id
             */
            o.setOrderClassifyId(dto.getUse().getOrderClassifyId());
            /**
             * 设置创建时间
             */
            o.setCreateDate(new Date());
            /**
             * 创建车次车辆司机订单分类关联表
             */
            orderClassifyAttachService.save(o);

            /**
             * 更新运输任务表
             */
            TaskTransportDTO d = new TaskTransportDTO();
            d.setTransportOrderIds(transportOrderIds);
            d.setTransportTripsId(o.getTripsId());
            d.setId(dto.getId());
            d.setTruckId(o.getTruckId());
            d.setAssignedStatus(2);
            workFeign.updateById(d);
            /**
             * 设置司机作业对象,用于返回创建司机作业
             */
            /**
             * 设置起始机构
             */
            jobDTO.setStartAgencyId(dto.getStartAgencyId());
            /**
             * 设置结束结构
             */
            jobDTO.setEndAgencyId(dto.getEndAgencyId());
            /**
             * 设置状态为待执行
             */
            jobDTO.setStatus(1);
            /**
             * 设置司机Id
             */
            jobDTO.setDriverId(o.getDriverId());
            /**
             * 设置运输任务
             */
            jobDTO.setTaskTransportId(dto.getId());
            /**
             * 设置创建时间
             */
            jobDTO.setCreateTime(new Date());
            driverJobDTO.add(jobDTO);
        }
        return driverJobDTO;
    }


    /**
     * 创建运输任务---管理运单
     *
     * @param list 线路和订单分类中间表的数据
     * @return 返回运输任务数据集合
     */
    private List<TaskTransportDTO> createATransportationTask(List<CacheLineUse> list) {
        List<TaskTransportDTO> l = new ArrayList<>();
        /**
         * 1.循坏查询 根据订单分类Id查询出对应的订单列表
         */
        for (CacheLineUse use : list) {
            QueryWrapper<OrderClassifyOrder> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("order_classify_id", use.getOrderClassifyId());
            /**
             * 获取订单和订单分类的中间表
             */
            List<OrderClassifyOrder> orderList = orderClassifyOrderService.list(queryWrapper);

            /**
             * 订单将运单状态改为已装车和调度状态改为已调度
             */
            List<String> orderIds = orderList.stream().map(orderClassifyOrder -> {
                return orderClassifyOrder.getOrderId();
            }).collect(Collectors.toList());
            /**
             * 返回这些已经更改为已调度的运单信息的运单Id集合
             */
            transportOrderIds = orderIds.stream().map(o -> workFeign.modifyStatusDispatch(o).getData().getId()).collect(Collectors.toList());

            /**
             * 根据订单分类id查询订单分类数据
             */
            OrderClassify classify = orderClassifyService.getById(use.getOrderClassifyId());
            /**
             * 开始创建运输任务表
             */
            TaskTransportDTO taskTransport = new TaskTransportDTO();
            /**
             * 设置运单id集合
             */
            taskTransport.setTransportOrderIds(transportOrderIds);
            /**
             * 设置起始机构
             */
            taskTransport.setStartAgencyId(classify.getStartAgencyId());
            /**
             * 设置结束机构
             */
            taskTransport.setEndAgencyId(classify.getEndAgencyId());
            /**
             * 设置任务状态为待执行
             */
            taskTransport.setStatus(1);

            /**
             * 设置任务状态为未分配
             */
            taskTransport.setAssignedStatus(1);
            /**
             * 保存
             */
            taskTransport = workFeign.save(taskTransport).getData();
            taskTransport.setUse(use);
            l.add(taskTransport);
        }
        return l;
    }

    /**
     * 线路规划
     *
     * @param orderClassify 订单分类集合
     * @param orders        订单集合
     * @return 返回订单分类和线路的中间表数据集合
     */
    private List<CacheLineUse> routePlanning(List<OrderClassify> orderClassify, List<Order> orders) {
        log.info("进入线路规划中!");
        List<CacheLineUse> cacheLineUses = new ArrayList<>();
        /**
         * 1.循环查询该订单分类集合的线路是否在线路信息表中
         */
        for (OrderClassify classify : orderClassify) {
            R<TransportLine> r = baseFeign.getTransportLineById(classify.getStartAgencyId(), classify.getEndAgencyId());
            TransportLine line = r.getData();
            /**
             * 如果为null,结束本次循环,继续下次循环
             */
            if (ObjectUtils.isEmpty(line)) {
                /**
                 * 如果为null则将这些运单设置为未匹配线路状态
                 */
                log.warn("该订单未在能匹配的线路中!");
                String classifyId = classify.getId();
                QueryWrapper<OrderClassifyOrder> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("order_classify_id", classifyId);
                List<OrderClassifyOrder> list = orderClassifyOrderService.list(queryWrapper);
                List<String> classifyIds = list.stream().map(o -> o.getOrderId()).collect(Collectors.toList());
                classifyIds.stream().forEach(o -> workFeign.modifyStatusNotMatched(o));
                continue;
            }
            /**
             * 如果不为null,则开始添加线路表和订单分类的中间表
             */
            CacheLineUse cacheLineUse = new CacheLineUse();
            /**
             * TODO 添加线路表(缓冲线路就不用添加了),以后有需要在改进
             */
            cacheLineUse.setCacheLineId(line.getId());
            /**
             * 设置订单分类Id
             */
            cacheLineUse.setOrderClassifyId(classify.getId());
            /**
             * 设置创建时间
             */
            cacheLineUse.setCreateDate(new Date());
            /**
             * 创建线路和订单分类的中间表
             */
            cacheLineUseService.save(cacheLineUse);
            cacheLineUses.add(cacheLineUse);
        }
        return cacheLineUses;
    }


    /**
     * 订单分类
     *
     * @param orderClassify 订单分类集合
     * @param orders        订单集合
     */
    private void orderClassification(List<OrderClassify> orderClassify, List<Order> orders) {
        /**
         *1.获取待装车以及支付过的的订单集合
         */
        List<Order> orderList = orderFeign.getOrderList(4, 2);
        if (ObjectUtils.isEmpty(orderList)) {
            return;
        }
        log.info("获取待装车以及支付过的的订单集合为:{}", orderList);
        /**
         * 2.获取当前任务日记信息
         */
        LinkedHashMap<String, Object> logInfo = OkHttpUtils.getXxlLogInfo();
        log.info("远程调用的任务调度信息为:{}", logInfo);
        OrderClassify orderClassify2 = new OrderClassify();
        log.info("日记id为:{}", String.valueOf(logInfo.get("id")));
        log.info("任务Id为:{}", String.valueOf(logInfo.get("jobId")));

        /**
         * 3.根据订单id查询出该订单所对应的订单寄件网点和收件网点,在对它们进行分类
         */
        List<OrderLocation> orderDTOS = new ArrayList<>();
        orderList.stream().forEach(order -> {
            R<OrderLocationVO> r = orderFeign.queryById(order.getId());
            OrderLocationVO vo = r.getData();
            OrderLocation orderLocation = new OrderLocation();
            BeanUtil.copyProperties(vo, orderLocation);
            orderDTOS.add(orderLocation);
            log.info("根据订单id查询出该订单所对应的订单寄件网点和收件网点,在对它们进行分类");
        });

        /**
         * 4.进行分类
         */
        List<List<OrderLocation>> list = new ArrayList<>();
        String sendAgencyId = "";
        String receiveId = "";

        for (OrderLocation location : orderDTOS) {
            if (sendAgencyId.equals(location.getSendAgentId()) && receiveId.equals(location.getReceiveAgentId())) {
                log.warn("订单一样,结束本次循环,继续下次循环");
                continue;
            }
            R<List<OrderLocation>> r = orderFeign.queryOrderLocationBySendAgencyIdAndReceiveId(location.getSendAgentId(), location.getReceiveAgentId());
            if (CollUtil.isNotEmpty(r.getData())) {
                list.add(r.getData());
            }
            sendAgencyId = location.getSendAgentId();
            receiveId = location.getReceiveAgentId();
            log.info("添加成功,起始机构Id:{},结束机构为Id:{}", sendAgencyId, receiveId);
        }

        if (CollUtil.isEmpty(list)) {
            return;
        }
        /**
         * 5.开始添加订单分类记录
         */
        for (List<OrderLocation> l : list) {
            OrderClassify o = new OrderClassify();
            /**
             * 设置订单总数
             */
            o.setTotal(l.size());
            /**
             * 设置任务Id
             */
            o.setJobId(String.valueOf(logInfo.get("jobId")));
            /**
             * 设置任务日记Id
             */
            o.setJobLogId(String.valueOf(logInfo.get("id")));
            /**
             * 设置起始机构
             * 由于已经将类分好了,第一个和其他都是没区别的
             */
            o.setStartAgencyId(l.get(0).getSendAgentId());
            /**
             * 设置结束结构
             * 和上述一样
             */
            o.setEndAgencyId(l.get(0).getReceiveAgentId());
            /**
             * 设置订单类别
             */
            o.setClassify(l.get(0).getSendAgentId() + "##" + l.get(0).getReceiveAgentId());
            /**
             * 设置创建时间
             */
            o.setCreateDate(new Date());
            /**
             * 添加订单分类
             */
            orderClassifyService.save(o);
            /**
             * 添加订单和订单分类中的关联
             */
            for (OrderLocation i : l) {

                Order order = orderFeign.getOrderById(i.getOrderId());
                if (ObjectUtils.isNotEmpty(order)) {
                    if (4 == order.getStatus()) {
                        OrderClassifyOrder j = new OrderClassifyOrder();
                        j.setOrderId(i.getOrderId());
                        j.setOrderClassifyId(o.getId());
                        orderClassifyOrderService.save(j);
                        log.info("添加订单分类和订单之间的中间表成功!");
                        /**
                         * 将订单状态改为运输中
                         */
                        orderFeign.modifyOrderStatus(i.getOrderId());
                        log.info("将订单状态改为运输中成功!!");
                    }
                }
            }
            orderClassify.add(o);
            log.info("添加订单分类成功,对象属性为:{}", o);
        }
        /**
         * 添加到这个订单集合中去
         */
        orders.addAll(orderList);
    }
}
